Repository navigation
fix(admin): unblock webhook drains when subscribers stall - #73
Conversation
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: needs real behavior proof before merge. Reviewed October 6, 2026, 11:59 PM ET / October 7, 2026, 03:59 UTC (Revision 33). ClawSweeper reviewWhat this changesThe PR bounds webhook notification attempts to two seconds, records timeouts, preserves untouched queued entries, and adds recovery coverage and operator documentation. Merge readiness⛔ Blocked before merge - 2 items remain The fix remains necessary and no actionable code defect remains. The prior contract, regression-test, and changelog concerns are resolved; real transport proof through the production Worker drain remains incomplete. Priority: P2 Review scores
Verification
How this fits togetherSuccessful admin mutations append changes to D1 and schedule background webhook notifications. The drain sends changes to enabled subscribers and saves delivery cursors and status for the dashboard. flowchart TD
A[Admin mutation] --> B[D1 change log]
A --> C[Background webhook drain]
B --> C
D[Subscriber URLs and cursors] --> C
C --> E[POST with deadline]
E --> F[Save attempted cursor and status]
F --> G[Dashboard delivery result]
F --> H[Untouched entries await next drain]
Before merge
Agent review detailsSecurityNone. Review metrics
Technical reviewBest possible solution: Keep the accepted notify-only deadline and preservation of untouched entries, supported by a real Worker transport trace demonstrating timeout recovery. Do we have a high-confidence way to reproduce the issue? Yes, source establishes the hang path: current main awaits an unbounded subscriber POST before advancing to other hooks. No failing runtime reproduction was executed during this read-only review. Is this the best way to solve the issue? Yes, the focused deadline and attempted-entry cursor tracking match the now-recorded delivery decision; the isolated regression addresses the previous race and remainder concerns. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning medium; reviewed against 7be7204292c9. LabelsLabel changes: No label changes. Label justifications:
EvidenceWhat I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (32 earlier review cycles; latest 8 shown)
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
Pushed @clawsweeper re-review |
Pushed @clawsweeper re-review |
Claw P2 on c2f74de: each drain made two unbounded Cloudflare DoH requests before delivery. A stalled resolver could pin waitUntil. Each A and AAAA lookup now uses AbortSignal.timeout (2s), matching the openclaw#73 subscriber POST bound. Hung preflight is lastStatus timeout and leaves the cursor put. Tests cover signal presence and a 20ms hang-until-abort drain. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Each subscriber POST now carries AbortSignal.timeout so a stalled admin-supplied URL cannot pin waitUntil after POST /admin/*. Timeouts use the existing unreachable last_status path. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca> (cherry picked from commit 2daa84b)
A fetch timeout now records last_status=timeout and leaves the cursor in place so the next drain retries. HTTP and unreachable failures still advance, matching the existing notify-only contract. Drop the release-owned changelog edit. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca> (cherry picked from commit 28d1693)
Abort hung subscriber POSTs, record last_status=timeout, and advance the cursor like every other failed notification. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca> (cherry picked from commit 0af4c1f)
A 2s cutoff still aborts a hung POST so drain can finish. Timeout no longer advances the cursor, so a slow subscriber is retried instead of permanently skipping the batch. HTTP and unreachable outcomes stay notify-only. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca> (cherry picked from commit 63f6c5e)
The released drain contract advances the cursor on failed posts so subscribers are not retried. Timeout now matches HTTP and unreachable: advance, record timeout, stop the batch. That avoids duplicating a post that completed after the cutoff. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca> (cherry picked from commit e12a356)
e12a356 to
0ad54b8
Compare
Claw P2 on c2f74de: each drain made two unbounded Cloudflare DoH requests before delivery. A stalled resolver could pin waitUntil. Each A and AAAA lookup now uses AbortSignal.timeout (2s), matching the openclaw#73 subscriber POST bound. Hung preflight is lastStatus timeout and leaves the cursor put. Tests cover signal presence and a 20ms hang-until-abort drain. Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca> (cherry picked from commit 9339996)
Co-authored-by: hannesrudolph <49103247+hannesrudolph@users.noreply.github.com>
…emplate docs: make PR descriptions plain-language first
Remove legacy Claude instruction filenames and aliases. Preserve retained AGENTS.md instructions and retire obsolete alias guidance where needed.
…aw#87) Refresh compatible dependencies and repair the Undici resolution Dependabot could not apply because Miniflare pinned vulnerable Undici 7.29.0. Development and deployment tooling now use patched Undici 7.29.1/8.10.2 and Sharp 0.35.4. The Node >=22.22.0 floor, React 18/19 SDK peer contract, and application/SDK versions are unchanged.
Bound CLI API requests and response-body reads to a new 30-second default deadline, with clear timeout errors and validated KRILLSWITCH_TIMEOUT_MS overrides from 0 through 2147483647 (0 restores unlimited waits). Clear timers on every exit path and cancel unread authentication-error bodies. Preserve existing HTTP errors, document upgrade and mutation-retry behavior in both CLI guides, and add regression coverage for timers, parsing bounds, and real HTTP body stalls. Validation: all documented checks and seven built-CLI HTTP scenarios passed on one AWS Crabbox lease; Codex autoreview clean through P2; exact-head CI green before merge. Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
Bound subscriber delivery to two seconds, advance only attempted entries, and preserve the queued remainder. Verify timeout recovery and healthy subscribers with isolated D1 fixtures; update operator docs and the changelog. Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
steipete
left a comment
There was a problem hiding this comment.
Approved: exact-head CI green and reviewed.
The merge-base changed after approval.
What Problem This Solves
Fixes post-mutation webhook drains hanging when a subscriber never responds.
User Impact
A timed-out attempt records
timeoutafter two seconds, advances through that attempted entry, and lets other subscribers proceed. Unattempted entries stay queued for the next drain.Why This Change Was Made
This preserves the notify-only delivery contract: attempted failures are not retried. The branch is updated to current main, with dashboard-operator documentation and an Unreleased entry. Thanks @SebTardif for the original fix; the contributor commits and co-author credit are preserved.
Evidence
The regression uses synthetic D1 fixtures without a competing background drain. It verifies one timed-out attempt, preservation of the next queued entry, delivery to a healthy subscriber, recovery, and no duplicate delivery. Against the original implementation it exceeds the three-second test limit; with the fix it passes. The complete gate on a clean AWS Crabbox install with Node 24.19.0 and pnpm 11.28.0 passes lint, typecheck, 277 tests, dashboard/demo/CLI builds, docs, and packed SDK verification. Independent Codex review is clean through P2.
No production endpoint was contacted. Exact-head CI and CodeQL passed at
192ef4db835ba7dde845f208367394359892e325. This PR awaits the required code-owner approval from the requested reviewers.